package de.hpi.generator.generator.distributors;

import java.util.ArrayList;

import de.hpi.generator.config.DistributionModel;
import de.hpi.generator.generator.storages.ValueSeriaInfo;
import de.hpi.generator.helpers.RandomSelector;

/**
 * a base object, which is responsible for data distribution
 * @author Michael Wolowyk
 */
public class DataDistributor {

    protected RandomSelector random = new RandomSelector();
    protected int aNumberOfValuesToGenerate = 0;
    protected int aAllValuesCount = 0;
    private DistributionModel aDistributionModel;

    public DistributionModel getaDistributionModel() {
        return aDistributionModel;
    }
    
    

    public int getAllValuesCount() {
        return aAllValuesCount;
    }

    public String getName() {
        return null;
    }
    private static ArrayList<DataDistributor> aAllDataDistributors = new ArrayList<DataDistributor>();

    /**
     * finds data distributor 
     * @param aDistributionModel
     * @return found data distributor
     * @throws Exception not supported distribution type
     */
    public static DataDistributor findDataDistributor(DistributionModel aDistributionModel) throws  Exception {
        DataDistributor result = null;
        // check aAllDataDistributors initialization
        if (aAllDataDistributors.isEmpty()) {
            // was not initialized, initialize it
            registerDataDistributors();
        }

        // find aDataDistributor for required type in the container
        for (DataDistributor aDataDistributor : aAllDataDistributors) {
            if (aDataDistributor.getName().compareToIgnoreCase(aDistributionModel.type) == 0) {
                // was found. Create copy of aSpecializedDataDistributor object
                // with specialized params
                result = aDataDistributor.createCopyWithSpecializedParams(aDistributionModel.createSpecializedDistributionModel());
                // end search
                break;
            }
        }
        // not suported distribution type
        if(result == null){
                throw new Exception(String.format("The distribution model  with type: %s not supported yet", aDistributionModel.type));
            }

        return result;
    }

    /**
     * creates a copy of aSpecializedDataDistributor object with specialized
     * params
     * 
     * @param aSpecializedDistributionModel
     *            specialized params
     * @return a copy of distribution model with specialized params
     * @throws Exception
     */
    protected DataDistributor createCopyWithSpecializedParams(DistributionModel aSpecializedDistributionModel) throws Exception {
        // creates a copy of aSpecializedDataDistributor object
        DataDistributor result = (DataDistributor) this.getClass().newInstance();
        // setup object with specialized params
        result.setupObjectWithSpecializedParams(aSpecializedDistributionModel);
        return result;
    }

    /**
     * setup object with specialized params
     * 
     * @param aSpecializedDistributionModel
     *            specialized params
     */
    protected void setupObjectWithSpecializedParams(DistributionModel aSpecializedDistributionModel) throws Exception{
        this.aDistributionModel = aSpecializedDistributionModel;        
    }

    /**
     * register special data distributors
     * @throws Exception 
     */
    private static void registerDataDistributors() throws Exception {
        aAllDataDistributors.add(new GaussianDistributor());
        aAllDataDistributors.add(new PoissonDistributor());
        aAllDataDistributors.add(new ZipfDistributor());
        aAllDataDistributors.add(new UniformDistributor());
        aAllDataDistributors.add(new SequenceDistributor());
        aAllDataDistributors.add(new ZeroDistributor());
    }

    /**
     * generates random value and a distribution value for it
     * 
     * @return random value seria info
     */
    public ValueSeriaInfo generateNextRandomValueSeriaInfo() throws Exception {
       throw new Exception("Must be overwritten");
    }


    /**
     * Returns how many times the value should be generated in a column dependent on distribution
     * subclass responsibility
     * @param aValue
     * @return how many times the value should be generated in a column dependent on distribution
     */
    public int getCountOfValue(int aValue) throws Exception {
       throw new Exception("Must be overwritten");
    }

    /**
     * Setups a range of values for calculations
     * 
     * @param aValuesCount
     *            a Values Count in the range
     * 
     */
    public void setupRange(int aValuesCount) throws Exception {
        aNumberOfValuesToGenerate = aValuesCount;
    }


}
